/**
 * 
 */
package UsedCar;

import java.awt.Color;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;

import uchicago.src.sim.analysis.OpenHistogram;
import uchicago.src.sim.analysis.OpenSequenceGraph;
import uchicago.src.sim.engine.AbstractGUIController;
import uchicago.src.sim.engine.ActionGroup;
import uchicago.src.sim.engine.SimInit;
import uchicago.src.sim.engine.SimpleModel;
import uchicago.src.sim.gui.DisplaySurface;
import uchicago.src.sim.space.Object2DTorus;
import uchicago.src.sim.util.Random;

/**
 * @author August F.Y. Chao
 * 
 */
public class Model extends SimpleModel {
	public static final boolean FLAG = false;

	// ---------------------------------------------------------------------------------------------------------------------------------------------//
	// 平台中的變數區
	// ---------------------------------------------------------------------------------------------------------------------------------------------//
	public static final int SELLER_NUM = 50;
	public static final int BUYER_NUM = 200;
	public static final int CAR_TYPE = 5;
	public static final float PRO_RATIO = (float) 0.5;
	public static final int KNOW_MAX = 10;
	public static final int QUALITY_MAX = 10;
	public static final boolean RESET_POST_YR = true;
	public static final boolean IS_BUYER_SHARE = true;
	public static final boolean IS_INFLU = true;
	public static final boolean IS_KM = true;
	public static final int FORUM_MAX_YR = 5;

	// ---------------------------------------------------------------------------------------------------------------------------------------------//
	// 載入平台的變數
	// ---------------------------------------------------------------------------------------------------------------------------------------------//
	private int sellerNum = SELLER_NUM;
	private int buyerNum = BUYER_NUM;
	private int carType = CAR_TYPE;
	private float proRatio = PRO_RATIO;
	private int knowMax = KNOW_MAX;
	private int qualityMax = QUALITY_MAX;
	private boolean resetPostYr = RESET_POST_YR;
	private boolean isBuyerShare = IS_BUYER_SHARE;
	private boolean isInfluence = IS_INFLU;
	private boolean isKM = IS_KM;
	private int forumMaxYr = FORUM_MAX_YR;

	// ---------------------------------------------------------------------------------------------------------------------------------------------//
	// 平台參數設定區變數區
	// ---------------------------------------------------------------------------------------------------------------------------------------------//
	public String[] getInitParam() {
		String[] initParams = { "SellerNum", "BuyerNum", "CarType", "ProRatio",
				"KnowMax", "QualityMax", "resetPostYr", "BuyerShare", "KM",
				"forumMaxYr" };
		return initParams;
	}

	// ---------------------------------------------------------------------------------------------------------------------------------------------//
	// 變數區
	// ---------------------------------------------------------------------------------------------------------------------------------------------//
	/**
	 * @param MAX_RUN_TICK
	 *            max run tick
	 */
	public int MAX_RUN_TICK = 2000;
	/**
	 * @param yr
	 *            model tick
	 */
	public int yr = 0;
	/**
	 * 寬*高
	 */
	public static int VECTOR_SPACE = 100;

	/**
	 * @param marketSpace
	 *            2D 空間 for all
	 */
	private Object2DTorus marketSpace;
	/**
	 * @param sellerSpace
	 *            只有賣家的空間
	 */
	private Object2DTorus sellerSpace;
	/**
	 * @param disSurf
	 *            顯示平台
	 */
	private DisplaySurface disSurf;

	/**
	 * @param sellerSeqAVG
	 *            賣家的平均價格順序圖
	 * @param sellerSeqSell
	 *            賣家的平均賣出銷售額
	 * @param sellerSeqCar
	 *            賣家的平均賣出車輛數
	 */
	private OpenSequenceGraph sellerSeqAVG, sellerSeqCar, sellerSeqSell;
	// private OpenSequenceGraph buyerSeqYr, buyerSeqPosts;

	/**
	 * @param knows
	 *            知識總量
	 */
	private Knowledges knows;
	/**
	 * @param qual
	 *            品質與知識、價格的相依功能
	 */
	private Quality qual;
	/**
	 * @param frm
	 *            知識平台 FoRuM
	 */
	private Forum frm = new Forum();
	/**
	 * @param sellerL
	 *            賣家名單
	 */
	private ArrayList<Seller> sellerL = new ArrayList<Seller>();
	/**
	 * @param buyerL
	 *            買家名單
	 */
	private ArrayList<Buyer> buyerL = new ArrayList<Buyer>();

	/**
	 * @param sellerKnowR
	 *            賣家含的知識比
	 */
	private float sellerKnowR = (float) 1;

	/**
	 * @param sellerKnowR
	 *            買家含的知識比
	 */
	private float buyerKnowR = (float) 0.2;

	/**
	 * @param carNumPerTic
	 *            每次要加的車
	 */
	private int carNumPerTic = buyerNum / sellerNum;

	/**
	 * @author fychao 賣家的所有統計資料
	 */
	class sellerStat {
		protected Hashtable<String, Long> totalSell = new Hashtable<String, Long>();
		protected Hashtable<String, Integer> carNum = new Hashtable<String, Integer>();

		public void addStat(String key, long sell, int num) {
			long preTotalSell = 0;
			int preCarNum = 0;
			if (totalSell.containsKey(key)) {
				preTotalSell += totalSell.get(key);
				preCarNum += carNum.get(key);
			}

			totalSell.put(key, (preTotalSell + sell));
			carNum.put(key, (preCarNum + num));
		}

		public void test(String key) {
			if (totalSell.containsKey(key)) {
				System.out.println("Key=" + key + " totalSell="
						+ totalSell.get(key) + " carNum=" + carNum.get(key));
			}
		}

		public void clnData() {
			Enumeration<String> keys = totalSell.keys();
			while (keys.hasMoreElements()) {
				String key = keys.nextElement();
				totalSell.put(key, (long) 0);
				carNum.put(key, 0);
			}
		}

		public float getCarNum(String key) {
			if (totalSell.containsKey(key)) {
				return (float) carNum.get(key);
			} else
				return 0;
		}

		public float getSell(String key) {
			if (totalSell.containsKey(key)) {
				return (float) totalSell.get(key);
			} else
				return 0;
		}

		public float getAvg(String key) {
			if (totalSell.containsKey(key)) {
				return (float) totalSell.get(key) / carNum.get(key);
			} else
				return 0;
		}

	}

	protected sellerStat sStat = new sellerStat();
	protected sellerStat bStat = new sellerStat();
	protected sellerStat bpStat = new sellerStat();

	/**
	 * 設是設定基本的模擬環境值
	 */
	public void setup() {
		// 產生亂數種子
		long s = System.currentTimeMillis();
		Random.setSeed(s);

		// 之前的都要先跑
		super.setup();

		// make all probes update themselves as the model runs.
		AbstractGUIController.UPDATE_PROBES = true;

		// 一開始不要出現
		if (disSurf != null)
			disSurf.dispose();
	}

	/**
	 * 建立模型
	 */
	public void buildModel() {
		// 設定討論區參數
		this.frm.setMaxYr(forumMaxYr);

		// 設定模型的內容
		marketSpace = new Object2DTorus(VECTOR_SPACE, VECTOR_SPACE);
		sellerSpace = new Object2DTorus(VECTOR_SPACE, VECTOR_SPACE);

		// 將平原秀出來
		// disSurf = new DisplaySurface(this, "市場位置顯示圖");
		// disSurf.addDisplayableProbeable(new Object2DDisplay(marketSpace),
		// "市場位置顯示圖");
		// disSurf.display();

		// 將賣家的賣出情況列出
		/**
		 * @param TYPE_ALTRUISM
		 *            利他型 0
		 * @param TYPE_ONLY_ADV
		 *            純廣告型 1
		 * @param TYPE_ONLY_B
		 *            分享基本知識 2
		 */
		sellerSeqAVG = new OpenSequenceGraph("賣家的平均銷價 Sell/CarNum", this);
		sellerSeqAVG.createSequence("利他賣家", this, "sellerA0");
		sellerSeqAVG.createSequence("廣告賣家", this, "sellerA1");
		sellerSeqAVG.createSequence("半利他賣家", this, "sellerA2");
		sellerSeqAVG.display();

		sellerSeqCar = new OpenSequenceGraph("賣家的平均出售車輛 CarNum/Tick", this);
		sellerSeqCar.createSequence("利他賣家", this, "sellerC0");
		sellerSeqCar.createSequence("廣告賣家", this, "sellerC1");
		sellerSeqCar.createSequence("半利他賣家", this, "sellerC2");
		sellerSeqCar.display();

		sellerSeqSell = new OpenSequenceGraph("賣家的平均營業額 Sell/Tick", this);
		sellerSeqSell.createSequence("利他賣家", this, "sellerS0");
		sellerSeqSell.createSequence("廣告賣家", this, "sellerS1");
		sellerSeqSell.createSequence("半利他賣家", this, "sellerS2");
		sellerSeqSell.display();

		// 買家買自何處

		// buyerSeqYr = new OpenSequenceGraph("買到車的對像 - 秏費多少時間", this);
		// buyerSeqYr.createSequence("品質價格", this, "buyerF0");
		// buyerSeqYr.createSequence("廣告", this, "buyerF1");
		// buyerSeqYr.createSequence("影響", this, "buyerF2");
		// buyerSeqYr.display();
		//
		// buyerSeqPosts = new OpenSequenceGraph("買到車要看多少文章", this);
		// buyerSeqPosts.createSequence("品質價格", this, "buyerR0");
		// buyerSeqPosts.createSequence("廣告", this, "buyerR1");
		// buyerSeqPosts.createSequence("影響", this, "buyerR2");
		// buyerSeqPosts.display();

		// 建立知識市場
		this.knows = new Knowledges(this.carType, this.proRatio, this.knowMax);

		// 產生品質與知識的相依
		this.qual = new Quality(this.knows, this.qualityMax);

		// 產生 Seller Agent
		for (int i = 0; i < this.getSellerNum(); i++) {
			// 產生評價策略準則
			int eval = 2;
			// 產生賣家經營策略準則
			int sellerType = getSellerType(i);
			Seller man = new Seller(sellerSpace, marketSpace, frm, this.knows,
					this.qual, isInfluence, sellerKnowR, eval, sellerType);
			// 產生車
			createCars(man, carNumPerTic);

			// 加到賣方名單中
			sellerL.add(man);

		}

		// 產生 Buyer Agent
		for (int i = 0; i < this.getBuyerNum(); i++) {
			// 產生評價策略準則
			// int eval = Random.uniform.nextIntFromTo(0, 2);
			int eval = 2;
			// 建立動機型態
			int motiType = getBuyerType(i);
			// int motiType = 1;
			buyerL.add(new Buyer(sellerSpace, marketSpace, frm, this.knows,
					this.qual, isInfluence, buyerKnowR, eval, isBuyerShare,
					resetPostYr, motiType));
		}

	}

	/**
	 * 模型的建立順序
	 */
	public void buildSchedule() {

		// 賣家的動作
		ActionGroup sellerActs = new ActionGroup();
		try {
			// 老一歲
			sellerActs.createActionForEach(sellerL, "doAging");
			if (isKM)
				sellerActs.createActionForEach(sellerL, "doKM_T");
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}

		// 買家的動作
		ActionGroup buyerActs = new ActionGroup();
		try {
			// 老一歲
			sellerActs.createActionForEach(buyerL, "doAging");
			if (isKM)
				buyerActs.createActionForEach(buyerL, "doKM_T");
			buyerActs.createActionForEach(buyerL, "doBuyCar");
			buyerActs.createActionForEach(buyerL, "doLeave");
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}

		// 每次結尾做的人
		ActionGroup tickActs = new ActionGroup();
		tickActs.createActionFor(this, "afterTick");

		// 畫出圖形的動作
		ActionGroup disActs = new ActionGroup();
		disActs.createActionFor(this, "drawGraphInterval");

		// 所有動作的集合
		ActionGroup allActs = new ActionGroup();
		allActs.addAction(sellerActs);
		allActs.addAction(buyerActs);
		allActs.addAction(disActs);
		allActs.addAction(tickActs);

		schedule.scheduleActionAtInterval(1.0, allActs);
	}

	public void drawGraphInterval() {
		int interval = 50;

		if (this.yr % interval == 1) {
			// disSurf.updateDisplay();
			sellerSeqAVG.record();
			sellerSeqAVG.updateGraph();
			sellerSeqCar.record();
			sellerSeqCar.updateGraph();
			sellerSeqSell.record();
			sellerSeqSell.updateGraph();

			//
			// buyerSeqYr.record();
			// buyerSeqYr.updateGraph();
			// buyerSeqPosts.record();
			// buyerSeqPosts.updateGraph();
		}
	}

	/**
	 * 對 Agent 產生幾輛車
	 * 
	 * @param man
	 *            一個 Agent
	 * @param num
	 *            車子的數量
	 */
	public void createCars(Agent man, int num) {
		for (int j = 0; j < num; j++) {
			Car aCar = getCar((Agent) man);
			// 送到庫裡
			man.addCar(aCar);
		}
	}

	/**
	 * 產生一輛車，順便標價
	 * 
	 * @param man
	 *            一個 Agent
	 * @return 一輛 有價的 Car
	 */
	public Car getCar(Agent man) {
		Random.createUniform();

		// 產生一輛有價的車
		float carPrice = 0;
		Car getCar = null;
		do {
			// 產生車型
			int ct = Random.uniform.nextIntFromTo(0,
					this.knows.getCarTypes() - 1);
			// 產生車
			Car genCar = new Car(this.qual, ct);
			// 評價
			carPrice = man.evaluate(genCar);
			if (carPrice > 0) {
				getCar = genCar;
				// 賣家標價
				genCar.setEvaledPrice(carPrice);
			}
		} while (carPrice == 0);

		return getCar;
	}

	/**
	 * 偉大的 main function
	 */
	public static void main(String[] args) {
		Model model = new Model();
		SimInit init = new SimInit();
		init.loadModel(model, null, false);
	}

	/**
	 * 在 tick 後的動作
	 */
	public void afterTick() {

		this.yr += 1;

		delBuyer();
		addMoreBuyer();
		addMoreCar();
		long time;
		if (FLAG) {
			time = System.currentTimeMillis();
		}
		frm.tickPosts();
		if (FLAG) {
			time = System.currentTimeMillis() - time;
			if (time > 1)
				System.out.println("(MODEL) in afterTick(): " + time + " "
						+ this.frm.posts.size());
			time = System.currentTimeMillis();
		}

		listSellerMatrix();
		// frm.listPost();

		// 停止條件
		if (this.yr >= MAX_RUN_TICK)
			this.stop();

	}

	/**
	 * 例出賣家矩陣
	 */
	public void listSellerMatrix() {
		Iterator<Seller> it = sellerL.iterator();
		// 再次開始統計
		sStat.clnData();
		while (it.hasNext()) {
			Seller man = it.next();
			String key = man.getRT();

			sStat.addStat(key, man.getTotal_surplus_value(),
					man.soldCarL.size());

		}
	}

	/**
	 * 加入更多的車
	 */
	public void addMoreCar() {
		Iterator<Seller> itr = sellerL.iterator();
		while (itr.hasNext()) {
			int perTick = Random.uniform.nextIntFromTo(0, carNumPerTic);

			Seller man = itr.next();
			if (man.carL.size() < 100) {
				createCars(man, perTick);
			}
		}
	}

	/**
	 * 刪掉買完的
	 */
	int buyY = 0;
	int buyN = 0;

	public void delBuyer() {
		ArrayList<Buyer> rBuyer = new ArrayList<Buyer>();
		Iterator<Buyer> itr = buyerL.iterator();
		int have_car = 0;

		while (itr.hasNext()) {
			Buyer man = itr.next();
			// 如果有買到車子的買家
			if (man.getMyColor() == Color.BLACK) {
				rBuyer.add(man);
				marketSpace.putObjectAt(man.getMyX(), man.getMyY(), null);
				if (man.carL.size() > 0) {
					have_car += 1;
					// Car c = man.carL.get(0);
					// @todo why buyer buy this car?
					// Seller frmSeller = (Seller) c.buyFrom;
					// String key = frmSeller.getPE() + frmSeller.getRT();

					bStat.addStat(man.getBuyerTypeString(), man.yr, 1);
					bpStat.addStat(man.getBuyerTypeString(), man.readPostsNum,
							1);
				}
			}
		}
		buyY += have_car;
		buyN += (rBuyer.size() - have_car);
		// System.out.println("\tY:N = " + buyY + ":" + buyN);

		// statB.send(rBuyer);
		buyerL.removeAll(rBuyer);
		// statB.listData();
	}

	/**
	 * 加入新的買家
	 */
	public void addMoreBuyer() {
		// int perTick = Random.uniform.nextIntFromTo(1, (int) (buyerNum * 1));
		int perTick = buyerNum / 2;
		// 產生 Buyer Agent
		for (int i = 0; i < perTick; i++) {
			// 產生評價策略準則
			int eval = 2;
			int motiType = getBuyerType(i);
			buyerL.add(new Buyer(sellerSpace, marketSpace, frm, this.knows,
					this.qual, isInfluence, buyerKnowR, eval, isBuyerShare,
					resetPostYr, motiType));
		}
	}

	// ---------------------------------------------------------------------------------------------------------------------------------------------//
	// 賣家的數字
	// ---------------------------------------------------------------------------------------------------------------------------------------------//
	public float sellerAX(String key) {
		/**
		 * @param TYPE_ALTRUISM
		 *            利他型 0
		 * @param TYPE_ONLY_ADV
		 *            純廣告型 1
		 * @param TYPE_ONLY_B
		 *            分享基本知識 2
		 */
		return sStat.getAvg(key);
	}

	public float sellerA0() {
		return sellerAX("A");
	}

	public float sellerA1() {
		return sellerAX("O");
	}

	public float sellerA2() {
		return sellerAX("B");
	}

	public float sellerCX(String key) {
		return sStat.getCarNum(key) / yr;
	}

	public float sellerC0() {
		return sellerCX("A");
	}

	public float sellerC1() {
		return sellerCX("O");
	}

	public float sellerC2() {
		return sellerCX("B");
	}

	public float sellerSX(String key) {
		return sStat.getSell(key) / yr;
	}

	public float sellerS0() {
		return sellerSX("A");
	}

	public float sellerS1() {
		return sellerSX("O");
	}

	public float sellerS2() {
		return sellerSX("B");
	}

	// ---------------------------------------------------------------------------------------------------------------------------------------------//
	// 買家的數字
	// ---------------------------------------------------------------------------------------------------------------------------------------------//
	/**
	 * 買家的動機型態有三： PQ 單純以產品品質與價格為主、 AD 以廣告為主、IN 朋友推薦 為主
	 */
	public float buyerFX(String key) {
		return bStat.getSell(key) / bStat.getCarNum(key);
	}

	public float buyerF0() {
		return buyerFX("PQ");
	}

	public float buyerF1() {
		return buyerFX("AD");
	}

	public float buyerF2() {
		return buyerFX("IN");
	}

	public float buyerRX(String key) {
		return bpStat.getSell(key) / bpStat.getCarNum(key);
	}

	public float buyerR0() {
		return buyerRX("PQ");
	}

	public float buyerR1() {
		return buyerRX("AD");
	}

	public float buyerR2() {
		return buyerRX("IN");
	}

	// ---------------------------------------------------------------------------------------------------------------------------------------------//
	// 賣家與買家的類型設定
	// ---------------------------------------------------------------------------------------------------------------------------------------------//
	/**
	 * 賣家的型態：利他、半利他、廣告
	 * 
	 * @param seed
	 *            可以是 for 中的 i，產生順序
	 * @return 型態
	 */
	public int getSellerType(int seed) {
		return (seed % 3);
	}

	/**
	 * 買家的型態：PQ, AD, IN 三型
	 * 
	 * @param seed
	 *            可以是 for 中的 i，產生順序
	 * @return 型態
	 */
	public int getBuyerType(int seed) {
		return Random.uniform.nextIntFromTo(1, 3);
		// return (seed%3);
	}

	/**
	 * 設定賣家的估價型態
	 * 
	 * @param seed
	 * @return
	 */
	public int getSellerEvalType(int seed) {
		return (seed % 3);
	}

	// ---------------------------------------------------------------------------------------------------------------------------------------------//
	// 平台中的變數區的導入
	// ---------------------------------------------------------------------------------------------------------------------------------------------//

	public int getBuyerNum() {
		return buyerNum;
	}

	public void setBuyerNum(int buyerNum) {
		this.buyerNum = buyerNum;
	}

	public int getSellerNum() {
		return sellerNum;
	}

	public void setSellerNum(int sellerNum) {
		this.sellerNum = sellerNum;
	}

	public int getCarType() {
		return carType;
	}

	public void setCarType(int carType) {
		this.carType = carType;
	}

	public float getProRatio() {
		return proRatio;
	}

	public void setProRatio(float proRatio) {
		this.proRatio = proRatio;
	}

	public int getKnowMax() {
		return knowMax;
	}

	public void setKnowMax(int knowMax) {
		this.knowMax = knowMax;
	}

	public int getQualityMax() {
		return qualityMax;
	}

	public void setQualityMax(int qualityMax) {
		this.qualityMax = qualityMax;
	}

	public boolean isBuyerShare() {
		return isBuyerShare;
	}

	public void setBuyerShare(boolean isBuyerShare) {
		this.isBuyerShare = isBuyerShare;
	}

	public boolean isResetPostYr() {
		return resetPostYr;
	}

	public void setResetPostYr(boolean resetPostYr) {
		this.resetPostYr = resetPostYr;
	}

	public boolean isInfluence() {
		return isInfluence;
	}

	public void setInfluence(boolean isInfluence) {
		this.isInfluence = isInfluence;
	}

	public boolean isKM() {
		return isKM;
	}

	public void setKM(boolean isKM) {
		this.isKM = isKM;
	}

	public int getForumMaxYr() {
		return forumMaxYr;
	}

	public void setForumMaxYr(int forumMaxYr) {
		this.forumMaxYr = forumMaxYr;
	}
}
